var String = Packages.java.lang.String;

function formatReal(x) {
	x = Number(x);
	var s = String.format("%f", x);
	if (s.contains(".")) {
		s = s.replaceAll("0+$", "");
		if (s.endsWith(".")) {
			s+= "0";
		}
	}
	if (s.length() > 10) {
		s = String.format("%g", x);
	}
	return s;
}

function formatRealToExponential(x) {
	if(x == null || x == undefined) {
		return formatRealToExponential(0);
	}
		
	x = Number(x);
	var s = x.toExponential(DEFAULT_PRECISION); //Hard-coded number of points after decimal
	s = s.replaceAll("e", "E");
	var index = s.indexOf("E");
	if(index != -1 && s.length - index == 3) {
		//Need to add "0"
		var str1 = s.substring(0, index + 2);
		var str2 = s.substring(index + 2, s.length);
		s = str1 + "0" + str2;
	}
	return s;
}

function getNumPointsForMultiCurves(curves) {
	var numCurves = curves.length;
	var numPoints = 0;
	for(var i = 0; i < numCurves; i++) {
		var curve = curves[i];
		if(curve != null || curve != undefined) {
			numPoints = curve.length;
			return numPoints;
		}
	}
	return numPoints;
}

function getValidCurvesFromAllCurves(curves) {
	var length = curves.length;
	var ret = [];
	for(var i = 0; i < length; i++) {
		var curve = curves[i];
		if(isValidCurve(curve))
			ret.push(curve);
	}
	
	return ret;
}

function getValidCurve(curves) {
	var numCurves = curves.length;
	for(var i = 0; i < numCurves; i++) {
		var curve = curves[i];
		if(isValidCurve(curve)) {
			return curve;
		}
	}
	return null;
}

function interp_x(curve, x) {
	// curve is a nested array [series][x = 0, y = 1]
	var X = 0;
	var Y = 1;
	// if curve has single point only then return Y value of that point
	if(curve.length == 1){
		return curve[0][Y];
	}
	// check extreme conditions first
	if (x <= curve[0][X]) {
		return curve[0][Y];
	}

	if (x >= curve[curve.length - 1][X]) {
		return curve[curve.length - 1][Y];
	}

	// must be in the middle. need to interpolate
	for ( var i = 1; i < curve.length; i++) {
		if (curve[i][X] < x) {
			continue;
		}
		
		if(curve[i][X] == x)
		return curve[i][Y];

		var xspan = curve[i][X] - curve[i - 1][X];
		var yspan = curve[i][Y] - curve[i - 1][Y];
		
		if(xspan == 0.0)
			continue;

		var ratio = (x - curve[i - 1][X]) / xspan;
		return ratio * yspan + curve[i - 1][Y];
	}
}

function scale_y(curve, factor) {
	// curve is a nested array [series][X = 0, Y = 1]
	var ret = [];

	var X = 0;
	var Y = 1;

	for ( var i = 0; i < curve.length; i++) {
		ret.push([ curve[i][X], factor * curve[i][Y] ]);
	}

	return ret;
}

function negateCurve(curve) {
	var ret = [];
	var X = 0;
	var Y = 1;
	curve = sortCurve(curve);
	for ( var i = 0; i < curve.length; i++) {
		var x = curve[i][X];
		var y = curve[i][Y];
		if(y > 0.0) {
			y = 0.0 - y;
		}
		ret.push([ x, y ]);
	}

	return ret;
}

function getMaximumYValueFromCurve(curve) {
	var length = curve.length;
	var maxY = 0.0;
	for(var i = 0; i < length; i++) {
		if(curve[i][1] > maxY)
			maxY = curve[i][1];
	}
	
	return maxY;
}

function Modify_Iso_Stress_Strain_Curve(curve, yieldStress) {
	return Modify_Iso_Stress_Strain_Curve_With_Slope(curve, yieldStress, false);
}

function Modify_Iso_Stress_Strain_Curve_With_Slope(curve, yieldStress, removeNegativeSlope) {
	// curve is a nested array [series][X = 0, Y = 0]
	var X = 0;
	var Y = 1;
	var EPSILON = 10e-14;
	//sort the curve 
	curve = sortCurve(curve);
	//Find  Maximum Stress Value from curve
	var  max_stress_from_curve = getMaximumYValueFromCurve(curve);
	//check if the first point of the curve is 0,0

	if (curve[0][X] != 0.0 || curve[0][Y] != 0.0) {
		curve.unshift([0.0, 0.0]);
	}
	
	//Define Variables
	var ePoint = 1;
	var nPoints = curve.length;
	while(ePoint < nPoints) {
		if(curve[ePoint][X] == 0.0 || curve[ePoint][Y] == 0.0) {
			ePoint++;
		} else
			break;
	}
	
	//Check for Bad Data
	if(ePoint == nPoints) {
		var msg = "Stress Strain data is invalid.";
		debugMsg(msg);
		throw msg;
		return;
	}
		
	var youngsMod = curve[ePoint][Y]/curve[ePoint][X]; //check if x point is non zero
	//save this point
	var xE = curve[ePoint][X];
	var yE = curve[ePoint][Y];
	
	if(yieldStress == null || yieldStress == 0.0) {
		yieldStress = evaluateYieldStressFromStressStrainCurve(curve, youngsMod);		
	}
	
	yieldStrain = yieldStress/youngsMod;
    var save_point = 0;
    for ( point = 1; point<nPoints; point++ ) {
    	if ( curve[point][Y] > yieldStress ) {
    		save_point = point; 
    		break;
    	}
    }
    
    if ( yieldStress > max_stress_from_curve ) {
    	debugMsg("yield Stress "+yieldStress+" is greater than Maximum Stress "+max_stress_from_curve+" from the curve");
    }
    
    var result = [];
    result.push([0.0, 0.0]); //First point is always zero
    /*if(xE < yieldStrain && yE/xE != yieldStress/yieldStrain) {
    	result.push([xE, yE]);
    } */   
    result.push([yieldStrain, yieldStress]);
    var prevX = yieldStrain;
    var prevY = yieldStress;
    for ( point = save_point; point<nPoints; point++ ) {
    	var y = curve[point][Y];
    	var x = curve[point][X];
    	var xx = x - y/youngsMod;
    	if ( xx/x < EPSILON )
    		xx = 0.;
    	if ( y != yieldStress && xx > 0. ) {
    		var addPoint = true;
    		if(removeNegativeSlope) {
    			var diffX = x - prevX;
        		var diffY = y - prevY;
        		var slope = diffY/diffX;
        		if(slope < 0) {
        			debugMsg("slope negative : "+x);
        			addPoint = false;
        		}        			
    		}
    		if(addPoint) {
    			result.push([x, y]);
    			prevX = x;
        		prevY = y;
    		}
    	}
    }

	return result;
}

function evaluateYieldStressFromStressStrainCurve(curve, youngsMod) {
	var X = 0;
	var Y = 1;
	var yieldStress = 0.0;
	var max_stress = 0.0;
	var eps_lower = 0.0;
	var sig_lower = 0.0;
	var epst_lower = 0.0;
	var eps_higher = 0.0;
	var sig_higher = 0.0;
	var epst_higher = 0.0;
	var OFFSET_STRAIN = 0.002 // 0.2% offset strain
	var elastic_strain = 0.0;
	var plastic_strain = 0.0;
	var yieldStrain = 0.0;
	var nPoints = curve.length;
	for(var point = 1; point < nPoints; point++) {
		var x = curve[point][X];
		var y = curve[point][Y];
		elastic_strain = y/youngsMod;
		plastic_strain = x - elastic_strain;
		if ( plastic_strain < OFFSET_STRAIN ) { 
			epst_lower = x;
			eps_lower  = plastic_strain; 
			sig_lower  = y; 
			if(y > max_stress) //Check for a peak stress (likely yield in low-alloy steels) that exists prior to the offset strain
				max_stress = y;
		}else { 
			epst_higher = x;
			eps_higher  = plastic_strain;
			sig_higher  = y;
			break;
		}
	}
	var delta_eps = eps_higher - eps_lower;
	
	if ( 0. == delta_eps ) {
		debugMsg("case 1 yieldStress = sig_higher");
		yieldStress = sig_higher;
	}else if (max_stress > sig_higher) {
		debugMsg("case 2 yieldStress = max_stress");
		yieldStress = max_stress; //Case for low-allow steels
	}else {
		debugMsg("case 3 yieldStress from Ratio");
		var delta_sig = sig_higher - sig_lower;
		var delta_epst = epst_higher - epst_lower;
		var ratio = (OFFSET_STRAIN - eps_lower)/delta_eps;
		yieldStress = sig_lower + ratio*delta_sig;
	}
	
	return yieldStress;
}

function findCommonRangeForCurves(curves) {
	var X = 0;
	var Y = 1;

	var minx, maxx;
	var bStarted = false;

	for ( var i = 0; i < curves.length; i++) {
		var curve = curves[i];
		if (curve == undefined || !curve)
			continue;
		if(curve.length >1){
			curve = sortCurve(curve);
		}
		var curvemin = curve[0][X];
		var curvemax = curve[curve.length - 1][X];

		if (bStarted) {
			minx = Math.max(minx, curvemin);
			maxx = Math.min(maxx, curvemax);
		} else {
			minx = curvemin;
			maxx = curvemax;
			bStarted = true;
		}
	}

	if (!bStarted) {
		return null;
	}
	var minTemp = Math.min(minx, maxx);
	var maxTemp = Math.max(minx, maxx);
	return [ minTemp, maxTemp ];
}

function parseRange(trange) {
	var min = trange[0];
	var max = trange[1];
	var minx = Math.min(min, max);
	var maxx = Math.max(min, max);
	return [minx, maxx];
}

function interpolateCurveWithCount(curve, minx, maxx, count) {
	minx = Number(minx);
	maxx = Number(maxx);
	count = Number(count);

	var range = maxx - minx;
	var ret = [];
	curve = sortCurve(curve);
	for ( var i = 0; i < count; i++) {
		var ratio = i / (count - 1);
		var x = minx + (ratio * range);
		if(curve.length == 1){
			var y = curve[0][1];
		}else{
			var y = interp_x(curve, x);
		}
		ret.push([ x, y ]);
	}

	return ret;
}

function interpolateCurve(curve, minx, maxx, count, increment) {
	if(curve && increment && !isNaN(minx) && !isNaN(maxx)) {
		return interpolateCurveWithIncrement(curve, minx, maxx, increment);
	}
	
	if (curve && !isNaN(minx) && !isNaN(maxx)) {
		if(count == null || Number(count) == 0)
			count = curve.length;
		return interpolateCurveWithCount(curve, minx, maxx, count);
	} else 
		return curve;
}

function interpolateCurveIfNeeded(curve, minx, maxx, count) {
	if (curve && count && !isNaN(minx) && !isNaN(maxx) && Number(count) > 1) {
		return interpolateCurveWithCount(curve, minx, maxx, count);
	}
	return curve;
}

function interpolateCurveWithIncrement(curve, minx, maxx, increment) {
	minx = Number(minx);
	maxx = Number(maxx);
	increment = Number(increment);

	var range = maxx - minx;
	var ret = [];
	curve = sortCurve(curve);
	var x = minx;
	while(x < maxx) {
		var y = interp_x(curve, x);
		ret.push([ x, y ]);
		x = x + increment;
	}
	
	var yMax = interp_x(curve, maxx);
	ret.push([maxx, yMax ]);

	return ret;
}

function isAllCurvesAtCommonXPoints(curves) {
	var isCommonAxis = true;
	var contextCurve = null;
	
	for ( var i = 0; i < curves.length; i++) {
		var curve = curves[i];
		if(!isValidCurve(curve)) {
			continue;
		}
		if(contextCurve == null) {
			contextCurve = curve;
			continue;
		} else if(!isTwoCurvesAtCommonAxis(contextCurve,curve)) {
			isCommonAxis = false;
			break;
		}			
	}
	
	return isCommonAxis;
}

function isTwoCurvesAtCommonAxis(firstCurve, secondCurve) {
	var X = 0;
	var isCommonAxis = true;
	if (!isValidCurve(firstCurve) || !isValidCurve(secondCurve))
		isCommonAxis = false;
	else if(firstCurve.length != secondCurve.length)
		isCommonAxis = false;
	else {
		firstCurve = sortCurve(firstCurve);
		secondCurve = sortCurve(secondCurve);
		var length = firstCurve.length;
		for( var i = 0; i < length; i++) {
			if(firstCurve[i][X] != secondCurve[i][X]) {
				isCommonAxis = false;
				break;
			}
		}
	}
	
	return isCommonAxis;
}

function isValidCurve(curve) {
	return curve != null && curve != undefined;
}

function interpolateAsFirstCurveIfNeeded(firstCurve, secondCurve, min, max, numPoints, increment) {
	if(firstCurve != undefined && firstCurve && firstCurve.length > 0)
		return interpolateAsFirstCurve(firstCurve, secondCurve, min, max);
	else
		return interpolateCurve(secondCurve, min, max, numPoints, increment);
}

function interpolateIfNeeded(curves, mainCurve, min, max, numPoints, increment) {
	var ret = mainCurve;
	if(!isAllCurvesAtCommonXPoints(curves)) {
		ret = interpolateCurve(mainCurve, min, max, numPoints, increment);
	} else if(isValidCurve(mainCurve) && numPoints && !isNaN(min) && !isNaN(max)) {
		mainCurve = sortCurve(mainCurve);
		var length = mainCurve.length;
		if(length == numPoints && mainCurve[0][0] == min && mainCurve[length - 1][0] == max) {
			ret = mainCurve;
		} else {
			ret = interpolateCurve(mainCurve, min, max, numPoints, increment);
		}			
	} else if(isValidCurve(mainCurve) && increment && !isNaN(min) && !isNaN(max)) {
		ret = interpolateCurve(mainCurve, min, max, numPoints, increment);
	}
	
	return ret;
 }

function interpolateIfNeededForMaxDataPoints(curves, mainCurve, min, max, numPoints, increment, maxPoints) {
	var ret = interpolateIfNeeded(curves, mainCurve, min, max, numPoints, increment);
	ret = interpolateForMaxDataPoints(ret, maxPoints);	
	return ret;
 }

function interpolateAsFirstCurveWithBound(firstCurve, secondCurve) {
	var firstCurveLength = firstCurve.length;
	var sMinx = secondCurve[0][0];
	var sMaxx = secondCurve[secondCurve.length - 1][0];
	var ret = [];
	// if range is not specifed then interpolate using first curve's min and max points
	for( var i=0; i< firstCurveLength; i++){
		var x = firstCurve[i][0];
		var y = 0;
		if(x >= sMinx && x <= sMaxx)
			y = interp_x(secondCurve, x);
		
		ret.push([ x, y ]);
	}
	
	return ret;
}

function interpolateAsFirstCurve(firstCurve, secondCurve, min, max) {
	var firstCurveLength = firstCurve.length;
	var ret = [];
	// if range is not specifed then interpolate using first curve's min and max points
	if(min == null && max == null){
		for( var i=0; i< firstCurveLength; i++){
			var x = firstCurve[i][0];
			var y = interp_x(secondCurve, x);
			ret.push([ x, y ]);
		}
	}else{
		var y;
		for( var i=0; i< firstCurveLength; i++){
			// if min == null only check that firstCurve[0][x] <= max
			var x = firstCurve[i][0];
			if(min == null && max >= x){
				y = interp_x(secondCurve, x);
			}else if(max == null && min <= x){
				// if max == null only check that firstCurve[0][x] >= min
				y = interp_x(secondCurve, x);
			}else if(min <= x && max >= x){
				y = interp_x(secondCurve, x);
			}else{
				continue;
			}
			ret.push([ x, y ]);
		}

	}
	return ret;
}

function interpolateForRangeAndMaxDataPoints(curve, minx, maxx, count, increment, maxNumPoints) {
	// check if numPoints is greater than 100 then interpolate else print curve values as it is..
	var retCurve = interpolateCurve(curve, minx, maxx, count, increment);
	maxNumPoints = parseInt(maxNumPoints);
		// interpolate curve
		var len = retCurve.length;
		if(len > maxNumPoints){
			var minx = retCurve[0][0];
			var maxx = retCurve[len-1][0];
			minx = Number(minx);
			maxx = Number(maxx);
			return interpolateCurveIfNeeded(retCurve, minx, maxx, maxNumPoints);
		}
	return retCurve;
}

function interpolateForMaxDataPoints(firstCurve, maxNumPoints) {
	// check if numPoints is greater than 100 then interpolate else print curve values as it is..
	maxNumPoints = parseInt(maxNumPoints);
		// interpolate curve
		var len = firstCurve.length;
		if(len > maxNumPoints){
			var minx = firstCurve[0][0];
			var maxx = firstCurve[len-1][0];
			minx = Number(minx);
			maxx = Number(maxx);
			return interpolateCurveIfNeeded(firstCurve, minx, maxx, maxNumPoints);
		}
	return firstCurve;
}

function interpolateFamilyOfCurvesForMaxDataPoints(curveFamily, curveInputName, maxNumPoints) {
	var clonedFamily = curveFamily.cloneFamily();
	return interpolateFamilyOfCurves(curveFamily, curveInputName, maxNumPoints);
}

function interpolateFamilyOfCurves(curveFamily, curveInputName, maxNumPoints) {
	var familyParameterGroups = curveFamily.getFamilyParameterGroups();
	var numGroups = familyParameterGroups.size();
	for(var i = 0; i < numGroups; i++) {
		var group = familyParameterGroups.get(i);
		var familyInput = group.getInputByName(curveInputName);
		var curve = familyInput.getValue();
		if(curve.length < maxNumPoints)
			continue;
		curve = interpolateForMaxDataPoints(curve, maxNumPoints);
		familyInput.updateValue(curve);
	}
	return curveFamily;
}

function reduceAndInterpolateFamilyOfCurves(curveFamily, curveInputName, maxNumPoints, maxNumGroups) {
	var ret = curveFamily;
	if(curveFamily == null)
		return ret;
	if(curveFamily.getFamilyParameterGroups().size() <= maxNumGroups) {
		ret = interpolateFamilyOfCurvesForMaxDataPoints(curveFamily, curveInputName, maxNumPoints);
	}else {
		var reducedFamily = reduceFamilyDataToMaxNumberOfFamilyParameterGroups(curveFamily, maxNumGroups);
		ret = interpolateFamilyOfCurves(reducedFamily, curveInputName, maxNumPoints);
	}
	
	return ret;
}

function reduceFamilyDataToMaxNumberOfFamilyParameterGroups(curveFamily, maxNumGroups) {
	var ret = curveFamily;
	if(curveFamily == null)
		return ret;
	if(curveFamily.getFamilyParameterGroups().size() <= maxNumGroups)
		return ret;
	
	var clonedFamily = curveFamily.cloneFamily();
	clonedFamily.reduceFamilyData(maxNumGroups);
	return clonedFamily;
}

function findAverageNumberofDataPoints(Curves, min, max) {
	var totalPoints = 0;
	var numCurves = 0;
	var AvgNumPoints = 0;
	if(min == null && max == null){
		// All x points present in curve will be total number of points
		for(var i=0;i<Curves.length;i++){
			if(Curves[i] == null || Curves[i] == undefined)
			continue;
			
			totalPoints += Curves[i].length;
			numCurves++;
		}

		if(numCurves > 0) {
			AvgNumPoints = totalPoints / numCurves;
		}
		return Math.round(AvgNumPoints);
	}else{
		for(var i=0;i<Curves.length;i++){
			var count=0;
			var Curve = Curves[i];
			if(Curve == null || Curve == undefined)
				continue;
			numCurves++;
			for(var j=0; j<Curve.length;j++){
				// if min == null only check that Curve[0][x] <= max
				if(min == null && max >= Curve[j][0]){
					count++;
				}else if(max == null && min <= Curve[j][0]){
					// if max == null only check that firstCurve[0][x] >= min
					count++;
				}else if(min <= Curve[j][0] && max >= Curve[j][0]){
					count++;
				}
			}
			totalPoints += count;
		}
		
		if(numCurves > 0) {
			AvgNumPoints = totalPoints / numCurves;
		}
		
		return Math.round(AvgNumPoints);
	}
}

function twoCurveAlgebra(curves, iPrefix) {
	var ret = [];
	// iPrefix 0 = Multiplication, 1 = division , 2 = addition , 3 = subtraction.
	// curve is a nested array [series][x = 0, y = 1]
	var X = 0;
	var Y = 1;
	var y = 0.0;
	var iNum = curves.length;
	if (curves.length != 0) {;
		var curve1 = curves[0];
		var curve2 = curves[1];
		if (curve1 == undefined || !curve1 || curve1.length == 0
				|| curve2.length == 0 || curve2 == undefined || !curve2)
			return ret;

		for ( var i = 0; i < curve1.length; i++) {
			var u = curve1[i][Y];
			var v = curve2[i][Y];
			switch (iPrefix) {
			case 0:
				y = u * v;
				break;
			case 1:
				y = u / v;
				break;
			case 2:
				y = u + v;
				break;
			case 3:
				y = u - v;
				break;
			default:
				y = u * v;
				break;
			}
			var x = curve1[i][X];
			ret.push([ x, y ]);
		}
	}

	return ret;
}

function curveMultiConstant(curve, iConst, iPrefix) {
	var ret = [];
	// iPrefix 0 = iconst * curve, 1 = 1 - curve , 2 = 1 / curve.
	// curve is a nested array [series][x = 0, y = 1]
	var X = 0;
	var Y = 1;

	for ( var i = 0; i < curve.length; i++) {
		var y = 0.0;
		switch (iPrefix) {
		case 0:
			if (iConst)
				y = iConst * curve[i][Y];
		case 1:
			y = 1 - curve[i][Y];
		case 2:
			y = 1 / curve[i][Y];
		default:
			y = 1 - curve[i][Y];
		}
		var x = curve[i][X];
		ret.push([ x, y ]);
	}

	return ret;
}

function card_csvCurve(curve,iPrefix) {
    // curve is a nested array [series][X = 0, Y = 1]
	  //int iPrefix = 0 for Abaqus, 1 for Ansys
    var s = "";
    var X = 0;
    var Y = 1;  
	  var ss = "";
	  if(iPrefix == 0)//For Abaqus
	  {
		X = 1 ; 
		Y = 0;		
	  }
	  else if(iPrefix == 1)//For Ansys
	  {
		ss = "TBPT, DEFI,";
	  }
	 
	  var i=0;
	  if(iPrefix == 0)
		  i = 2; //exclude zero point and yield point too
	  else if(iPrefix == 1)
		  i = 1; //Exclude first point as first point should be yield stress
    for(i;i<curve.length;i++)
	  {		
          s += ss + formatRealToExponential(curve[i][X]) + ", " + formatRealToExponential(curve[i][Y]) + "\n";
	  }
    
    return s;   
}

function sortFunction(a, b) {
    if (a[0] === b[0]) {
        return 0;
    }
    else {
        return (a[0] < b[0]) ? -1 : 1;
    }
}

function sortCurve(curve) {
	if(curve == null)
		return curve;
	var length = curve.length;
	var result = [];
	for(var i = 0; i< length; i++) {
		result.push(curve[i]);
	}
	
	result = result.sort(sortFunction);
	return result;
}

function debugMsg(msg) {
	try{
	if(DEBUG !=undefined && DEBUG == true )
		print(msg);
	}catch(e) {
		//ignore
	}		
}

function getCurveLengthAsString(curve, excludeFirstPoint) {
	var length = curve.length;
	if(excludeFirstPoint)
		length = length - 1;

	return parseInt(length).toString();
}

function convertToFixedLengthString(value, length) {
	var str = java.lang.Double.toString(value);
	if (str.length() > length) {
		var dotIndex = str.indexOf(".");
		if (dotIndex > -1 && dotIndex < length && (!str.contains(("E")))) {
			str = String.format("%"+length+"f", value);
			str = str.replaceFirst("0+$", "");
		}
	}
	if (str.length() > length) {
		var POUNDS = "##################################";
	
		var numDigits = length-3;
		while(numDigits > 0) {
			var formatter = new java.text.DecimalFormat("0."+POUNDS.substring(0, numDigits)+"E0");
		    str = formatter.format(value);
		    if (str.length() <= length) {
		    	break;
		    }
		    numDigits--;
		}
	}

	
	str = java.lang.String.format("%"+length+"s", str);
	
	return str;
}
